use utils::bincodec::Decode;

use crate::app::{
    cargo::driver::pick_motor,
    error::{AppErr, BaseErr, ErrorCode},
    proto::{cmd::cargo, EmptyResponse, RequestRef, Response}, service::dispatcher::send_to_ipc_res,
};


use super::driver::{self, cargo_motor, compr, scan_uart, sensor};

pub fn on_simple_req(request: &RequestRef) -> Result<(), AppErr> {
    let cmd = request.cmd_or_other();
    match cmd {
        cargo::SCAN_LED_CTRL => request.parse_body().and_then(|req| on_scan_led_ctrl(req)),
        _ => Err(AppErr::Base(BaseErr::InvalidCmd)),
    }
}

pub fn on_request(request: &RequestRef) -> Result<(), AppErr> {

    let cmd = request.cmd_or_other();

    match cmd {
        cargo::PICK_CTRL => request.parse_body().and_then(|req| on_pick_ctrl(req)),
        cargo::COMPR_CTRL => request.parse_body().and_then(|req| on_compr_ctrl(req)),
        cargo::CARGO_CTRL => request.parse_body().and_then(|req| on_cargo_ctrl(req)),
        cargo::ROW_COL_CTRL => request.parse_body().and_then(|req| on_row_col_ctrl(req)),
        _ => Err(AppErr::Base(BaseErr::InvalidCmd)),
    }
}

pub fn on_idle() {
    if !sensor::is_rotation_button_click() {    
        return;
    }
    cargo_motor::auto_run();
}

#[derive(Decode)]
struct RowCol {
    row: i32,
    col: i32,
}

fn on_row_col_ctrl(req: RowCol) -> Result<(), AppErr> {
    driver::cargo_pick_motor_run(req.row, req.col)?;
    Ok(())
}

pub fn on_scan_led_ctrl(ctrl: u8) -> Result<(), AppErr> {
    match ctrl {
        0 => compr::led_open(),
        1 => compr::led_close(),
        _ => return Err(AppErr::Base(BaseErr::Invalid)),
    }
    Ok(())
}

pub fn on_scan(request: &RequestRef) {
    match scan_uart::scan() {

        Ok(value) => {
            let resp = Response::new(request, value);
            send_to_ipc_res(&resp);
        },

        Err(e) => {
            send_to_ipc_res(&EmptyResponse::new_with_ec(request, e.ec()));
        }
    };
}

fn on_pick_ctrl(position: i32) -> Result<(), AppErr> {
    pick_motor::run(position)?;
    Ok(())
}

fn on_compr_ctrl(position: u8) -> Result<(), AppErr> {
    match position {
        0 => compr::cy_close(),
        1 => compr::cy_to_top()?,
        2 => compr::cy_to_bottom()?,
        3 => compr::cy_to_inner()?,
        4 => compr::cy_to_extern()?,
        5 => compr::air_open(),
        6 => compr::air_drop(),
        7 => compr::air_close(),
        8 => compr::cy_run_pick()?,
        _ => return Err(AppErr::Base(BaseErr::Invalid)),
    };
    Ok(())
}

fn on_cargo_ctrl(position: i32) -> Result<(), AppErr> {
    cargo_motor::run(position)?;
    Ok(())
}
